package route

import (
	"fmt"
	"gitee.com/jamon/espoir_go/common"
	"gitee.com/jamon/espoir_go/common/utils"
	"gitee.com/jamon/espoir_go/global"
	"gitee.com/jamon/espoir_go/logger"
	"net/rpc"
	"sync"
)

// FindTargetNode 每个插件一般需要实现这个接口
type FindTargetNode interface {
	GetTargetNodeId(routeID uint32) uint32
}

func GetAddressKey(routeID uint32, subAddress uint32) string {
	return fmt.Sprintf("%v_%v", routeID, subAddress)
}

type Controller struct {
	nodeData *global.NodeData

	// 可用的service节点 {key(group_id): value(node_id array)}
	availableNodeMap utils.Map[uint32, []string]

	// 可用的proxy节点 []node_id
	availProxyNodes []string

	// 地址缓存 {key(address=routeID_subAddress): value(node_id)}
	addressCache utils.Map[string, string]

	// rpc 连接缓存 {key(node_id): value(rpc_client)}
	rpcClientCache utils.Map[string, *rpc.Client]

	userManager *global.UserManager
}

var (
	routeCtl *Controller
	once     sync.Once
)

func GetController() *Controller {
	once.Do(func() {
		routeCtl = &Controller{
			nodeData:         global.GetNodeData(),
			availableNodeMap: utils.NewMap[uint32, []string](),
			availProxyNodes:  make([]string, 0),
			addressCache:     utils.NewMap[string, string](),
			rpcClientCache:   utils.NewMap[string, *rpc.Client](),
			userManager:      global.GetUserManager(),
		}
	})

	return routeCtl
}

func (ctl *Controller) getCachedAddress(routeID uint32, subAddress uint32) (nodeID string) {
	addressKey := GetAddressKey(routeID, subAddress)
	nodeID, exists := ctl.addressCache.Get(addressKey)
	if exists {
		return nodeID
	}
	return
}

func (ctl *Controller) GetTargetNodeId(routeGroupID uint32, subAddress uint32) (nodeID string) {
	// 1. 从缓存中获取
	nodeID = ctl.getCachedAddress(routeGroupID, subAddress)
	if nodeID != "" {
		return
	}

	// 2. 从可用节点中随机选取一个节点
	availableNodeSet, exists := ctl.availableNodeMap.Get(routeGroupID)
	if !exists {
		ctl.availableNodeMap.Put(routeGroupID, ctl.nodeData.Config.RemoteRpcNodeIDs)
		availableNodeSet = ctl.nodeData.Config.RemoteRpcNodeIDs
	}

	if len(availableNodeSet) > 0 {
		index := uint32(utils.RandInt(0, len(availableNodeSet)))
		nodeID = availableNodeSet[index]
	}

	if nodeID == "" {
		logger.Error("no available node for routeGroupID: ", logger.Kwargs{"routeGroupID": routeGroupID})
		return
	}

	// 记录到db, 如果db中已存在则以db为准

	return
}

func (ctl *Controller) connectRemoteNode(nodeID string) (client *rpc.Client, err error) {
	nodeAddr := ctl.nodeData.Config.GetRemoteNodeAddr(nodeID)
	if nodeAddr == nil {
		logger.Error("connectRemoteNode: nodeAddr == nil")
		return nil, fmt.Errorf("nodeAddr == nil")
	}
	address := fmt.Sprintf("%v:%v", nodeAddr.Host, nodeAddr.Port)
	client, err = rpc.Dial("tcp", address)
	if err != nil {
		logger.Error("connectRemoteNode: ", logger.Kwargs{"err": err})
		return
	}
	ctl.rpcClientCache.Put(nodeID, client)
	return
}

func (ctl *Controller) canDirectConnectRemoteNode(toNode string) bool {
	// 如果本节点是service类型节点，且目标节点和本节点之间没有rpc连接，则不能直连，需要请求转发往proxy
	if ctl.nodeData.Config.Type == common.NodeType_Service {
		_, ok := ctl.rpcClientCache.Get(toNode)
		if !ok {
			return false
		}
	}
	return true
}

func (ctl *Controller) getRouteRpcConn(toNode string, res *common.MsgResponse) (conn *rpc.Client, err error) {
	if toNode == "" {
		err = fmt.Errorf("RemoteServiceHandle: req.To == 0")
		logger.Error(err.Error())
		return
	}
	nextNodeID := toNode
	canDirectConnect := ctl.canDirectConnectRemoteNode(toNode)
	if !canDirectConnect {
		// 不能直连，需要请求转发往proxy, 随机选择一个proxy节点
		if len(ctl.availProxyNodes) == 0 {
			ctl.availProxyNodes = make([]string, len(ctl.nodeData.Config.RemoteRpcNodeIDs))
			copy(ctl.availProxyNodes, ctl.nodeData.Config.RemoteRpcNodeIDs)
			logger.Info("getRouteRpcConn: availProxyNodes: ",
				logger.Kwargs{"availProxyNodes": ctl.availProxyNodes, "remoteRpcNodeIDs": ctl.nodeData.Config.RemoteRpcNodeIDs})
		}

		if len(ctl.availProxyNodes) == 0 {
			err = fmt.Errorf("RemoteServiceHandle: no available proxy node")
			logger.Error(err.Error())
			return
		}
		index := uint32(utils.RandInt(0, len(ctl.availProxyNodes)))
		nextNodeID = ctl.availProxyNodes[index]
	}

	conn, ok := ctl.rpcClientCache.Get(nextNodeID)
	if !ok {
		conn, err = ctl.connectRemoteNode(nextNodeID)
	}
	return
}

func (ctl *Controller) RemoteServiceHandle(req *common.MsgRequest, res *common.MsgResponse) (err error) {
	conn, err := ctl.getRouteRpcConn(req.To, res)
	if err != nil {
		logger.Error("RemoteServiceHandle: ", logger.Kwargs{"err": err})
		return
	}
	logger.Debug("RemoteServiceHandle: ", logger.Kwargs{"req_data": string(req.Data), "msg_id": req.MsgId})
	err = conn.Call("RequestHandler.HandleMsg", req, res)
	return
}

func (ctl *Controller) RemotePushHandle(req *common.PushMsgRequest, res *common.MsgResponse) (err error) {
	conn, err := ctl.getRouteRpcConn(req.To, res)
	if err != nil {
		logger.Error("RemotePushHandle: ", logger.Kwargs{"err": err})
		return
	}
	err = conn.Call("PushHandler.HandleMsg", req, res)
	return
}

func (ctl *Controller) RemoteInternalHandle(req *common.InternalNotifyRequest, res *common.MsgResponse) (err error) {
	conn, err := ctl.getRouteRpcConn(req.To, res)
	if err != nil {
		logger.Error("RemoteInternalHandle: ", logger.Kwargs{"err": err})
		return
	}
	err = conn.Call("InternalHandler.HandleMsg", req, res)
	return
}

// SingleCastToClient 单播推送到客户端
func (ctl *Controller) SingleCastToClient(userID uint64, msgID uint32, jsonEncodeData []byte) (err error) {
	// 1. 获取用户所在的proxy节点
	sessionID, exists := ctl.userManager.GetSessionIDByUserID(userID)
	if !exists {
		logger.Error("SingleCast: user not exists, maybe offline", logger.Kwargs{"user_id": userID})
		return fmt.Errorf("user maybe offline")
	}
	proxyNum, _, err := common.ParseSessionID(sessionID)
	proxyID := fmt.Sprintf("%v_%v", common.NodeType_Proxy, proxyNum)
	msg := common.PushMsgRequest{
		MsgId:      msgID,
		From:       ctl.nodeData.Config.NodeID,
		To:         proxyID,
		Type:       common.MsgType_PUSH,
		Data:       jsonEncodeData,
		IsAll:      false,
		SessionIDs: []string{sessionID},
	}
	// 2. 转发
	err = ctl.RemotePushHandle(&msg, &common.MsgResponse{})
	if err != nil {
		logger.Error("SingleCast ", logger.Kwargs{"err": err})
		return
	}
	return
}

// NarrowCastToClient 窄播推送到客户端(推送少量用户)
func (ctl *Controller) NarrowCastToClient(userIDs []uint64, msgID uint32, jsonEncodeData []byte) (err error) {
	// 1. 获取用户所在的proxy节点
	sessionIDs := make([]string, 0)
	for _, userID := range userIDs {
		sessionID, exists := ctl.userManager.GetSessionIDByUserID(userID)
		if !exists {
			logger.Error("NarrowCast: user not exists, maybe offline", logger.Kwargs{"user_id": userID})
			continue
		}
		sessionIDs = append(sessionIDs, sessionID)
	}

	if len(sessionIDs) == 0 {
		return
	}

	var proxyInfo map[string][]string
	proxyInfo = make(map[string][]string)
	for _, sessionID := range sessionIDs {
		id := common.GetProxyID(sessionID)
		if id != "" {
			proxyInfo[id] = append(proxyInfo[id], sessionID)
		}
	}
	for proxyID, idList := range proxyInfo {
		msg := common.PushMsgRequest{
			MsgId:      msgID,
			From:       ctl.nodeData.Config.NodeID,
			To:         proxyID,
			Type:       common.MsgType_PUSH,
			Data:       jsonEncodeData,
			IsAll:      false,
			SessionIDs: idList,
		}
		// 2. 转发
		err = ctl.RemotePushHandle(&msg, &common.MsgResponse{})
		if err != nil {
			logger.Error("NarrowCast ", logger.Kwargs{"err": err})
			return
		}
	}

	return
}
